package net.bcxuexi.server.stream.outwrap;

import java.util.List;

import net.bcxuexi.config.Config;
import net.bcxuexi.exception.MyException;
import net.bcxuexi.ports.model.IPort;
import net.bcxuexi.server.model.SocketModel;
import net.bcxuexi.server.stream.StreamData;
import net.bcxuexi.tools.IdGen;
import net.bcxuexi.tools.StringUtils;

/**
 * 输出流数据打包
 */
public class OutputDataWrap {
	/**
	 * 服务器向客户端发送消息。
	 * 发送需要内网穿透的端口号
	 * 注意：只在control类型下发送
	 * 协议：
	 * <sendPortToClient>
	 * <iport>
	 *  <!--服务端数据通信主机,客户端需要建立到此主机的连接进行数据交互-->
	 *  <serverDataIp></serverDataIp>
	 *  <!--服务端数据通信端口,客户端需要建立到此端口的连接进行数据交互-->
	 *  <serverDataPort></serverDataPort>
	 *  <!--需要进行通信的内网ip-->
	 *  <innerIp></innerIp>
	 *  <!--需要进行通信的内网端口-->
	 *  <innerPort></innerPort>
	 * </iport>
	 * </sendPortToClient>
	 */
	public StreamData scPorts(SocketModel socketModel,List<IPort> iports){
		StringBuffer buffer = new StringBuffer();
		buffer.append("<sendPortToClient>");
		for(int i=0;i<iports.size();i++){
			IPort iport = iports.get(i);
			buffer.append("<iport>");
			buffer.append("<serverDataIp>");
			buffer.append(iport.getServerSocketIp());
			buffer.append("</serverDataIp>");
			buffer.append("<serverDataPort>");
			buffer.append(iport.getServerSocketPort());
			buffer.append("</serverDataPort>");
			buffer.append("<innerIp>");
			buffer.append(iport.getIp());
			buffer.append("</innerIp>");
			buffer.append("<innerPort>");
			buffer.append(iport.getPort());
			buffer.append("</innerPort>");
			buffer.append("<proxy>");
			buffer.append(iport.getProxy());
			buffer.append("</proxy>");
			buffer.append("</iport>");
		}
		buffer.append("</sendPortToClient>");
		byte[] bytes = buffer.toString().getBytes();
		byte[] newBytes = markStartEnd(bytes, (byte)1);//控制型通信消息
		StreamData streamData = new StreamData();
		streamData.setData(newBytes);
		streamData.setTotal(newBytes.length);
		streamData.setSocketModel(socketModel);
		return streamData;
	}
	/**
	 * 服务器向客户端发送消息。
	 * 内网客户端连接服务器后，服务器生成connId,并发送给客户端
	 * 注意：在proxy类型下不用发送
	 * 协议：
	 * <connId>8位uuid</connId>
	 */
	public StreamData scConnId(SocketModel socketModel){
		String connId = IdGen.generateShortUuid();
		socketModel.setConnId(connId);
		StringBuffer buffer = new StringBuffer();
		buffer.append("<connId>");
		buffer.append(connId);
		buffer.append("</connId>");
		byte[] bytes = buffer.toString().getBytes();
		byte[] newBytes = markStartEnd(bytes, (byte)1);//控制型通信消息
		StreamData streamData = new StreamData();
		streamData.setData(newBytes);
		streamData.setTotal(newBytes.length);
		streamData.setSocketModel(socketModel);
		return streamData;
	}
	
	/**
	 * 数据信息封装(代理socket得到的输入流数据需要封装之后传递到内网)
	 * 第0个字节为2:代表数据通信
	 * 第1到8个字节:代表proxy socket的connId
	 * 第9到20个字节:代表内网服务器的4位ip 192168001155
	 * 第21到25个字节:代表内网服务器的端口 如:61235,08080
	 * @param proxyInputData
	 * @param proxyConnId
	 * @param innerIp
	 * @param innerPort
	 * @return
	 * 		封装之后的数据
	 */
	public StreamData scDataWrap(StreamData proxyInputData,String proxyConnId,String innerIp,int innerPort) throws MyException{
		//在输入流基础上添加26个字节
		int topLength = 25;
		byte[] newByte = new byte[proxyInputData.getTotal()+topLength];
		StringBuffer buffer = new StringBuffer();
		buffer.append(proxyConnId);//第1到8个字节:代表proxy socket的connId
		String sIp[] = innerIp.split("\\.");
		if(sIp.length!=4){
			throw new MyException("scDataWrap 数据信息封装,ip错误"+innerIp);
		}
		for(int i=0;i<4;i++){
			String ip = sIp[i];
			if(ip.length()==1){
				sIp[i] = "00"+ip;
			}else if(ip.length()==2){
				sIp[i] = "0"+ip;
			}else if(ip.length()==3){
			
			}else{
				throw new MyException("scDataWrap 数据信息封装,ip错误"+innerIp);
			}
		}
		//第9到20个字节:代表内网服务器的4位ip 192168001155
		buffer.append(sIp[0]);
		buffer.append(sIp[1]);
		buffer.append(sIp[2]);
		buffer.append(sIp[3]);
		
		//第21到25个字节:代表内网服务器的端口 如:61235,08080
		String sInnerPort = ""+innerPort;
		if(sInnerPort.length()==1){
			sInnerPort="0000"+sInnerPort;
		}else if(sInnerPort.length()==2){
			sInnerPort="000"+sInnerPort;
		}else if(sInnerPort.length()==3){
			sInnerPort="00"+sInnerPort;
		}else if(sInnerPort.length()==4){
			sInnerPort="0"+sInnerPort;
		}
		buffer.append(sInnerPort);
		byte[] topMsg = buffer.toString().getBytes();
		if(topMsg.length!=topLength){
			throw new MyException("scDataWrap 数据信息封装,消息转换长度错误.长度为"+topMsg.length);
		}
		for(int i=0;i<topLength;i++){
			newByte[i] = topMsg[i];
		}
		for(int i=0;i<proxyInputData.getTotal();i++){
			newByte[i+topLength] = proxyInputData.getData()[i];
		}
		
		byte[] newBytes = markStartEnd(newByte, (byte)2);//数据型通信消息
		
		StreamData streamData = new StreamData();
		streamData.setData(newBytes);
		streamData.setTotal(newBytes.length);
		streamData.setProxyConnId(proxyConnId);
		
		return streamData;
	}
	public StreamData scProxyClosed(String proxyConnId){
		StringBuffer buffer = new StringBuffer();
		buffer.append("<proxyClosed>");
		buffer.append("<proxyConnId>");
		buffer.append(proxyConnId);
		buffer.append("</proxyConnId>");
		buffer.append("</proxyClosed>");
		byte[] bytes = buffer.toString().getBytes();
		byte[] newBytes = markStartEnd(bytes, (byte)1);//数据型通信消息
		StreamData streamData = new StreamData();
		streamData.setData(newBytes);
		streamData.setTotal(newBytes.length);
		return streamData;
	}
	/**
	 * 发送心跳消息，如果发送失败则关闭socket
	 * @param connId
	 * @return
	 */
	public StreamData scHeartbeat(String connId){
		StringBuffer buffer = new StringBuffer();
		buffer.append("<heartbeat>");
		buffer.append("<connId>");
		buffer.append(connId);
		buffer.append("</connId>");
		buffer.append("</heartbeat>");
		byte[] bytes = buffer.toString().getBytes();
		byte[] newBytes = markStartEnd(bytes, (byte)1);//数据型通信消息
		
		StreamData streamData = new StreamData();
		streamData.setData(newBytes);
		streamData.setTotal(newBytes.length);
		return streamData;
	}
	/**
	 * 为消息添加开始结束标记 及 类型标记
	 * 开始标记：头3位为1。结束标记：后3位为0
	 * @param msg
	 * @param type 1:控制消息  2:数据通信消息
	 * @return
	 */
	private byte[] markStartEnd(byte msg[],byte type){
		int length = msg.length+Config.start.length+Config.end.length+1;
		byte[] newBytes = new byte[length];
		for(int i=0;i<Config.start.length;i++){
			newBytes[i] = Config.start[i];
		}
		newBytes[Config.start.length]=type;
		for(int i=0;i<msg.length;i++){
			newBytes[i+Config.start.length+1]=msg[i];
		}
		//添加结束标记
		for(int i=0;i<Config.end.length;i++){
			newBytes[length-i-1] = Config.end[Config.end.length-i-1];
		}
		return newBytes;
	}
}
